#include <stdio.h>
#include <stdlib.h>
#include "dominion.h"
#include "rngs.h"
#include "math.h"
#include <assert.h>
#include <string.h>

/*
Council Room
+4 Cards, +1 Buy
Each other player draws a card

pre-requirements: Council Room card must be in current player's hand

post-requirements:
	for all players:
	Test 1, 2:
		Total card count should remain the same

	for current player:
	Test 3, 4, 5:
		council room card should be placed into the playedHand: 
		hand should have 3 more cards than before (+4 cards, -1 council room card)
		actions should be the same
		buy should increase by one

	for other players:
		hand should increase by 1
		if deck before >=1 then after the deck should have one less
			else deckAfter should equal discard -1 


*/





int MAX_SUPPLY = 27;
void prepRandom(int strm, int seed){
  SelectStream(strm);
  PutSeed((long)seed);
}

int getRandomNumber(int min, int max){
	int number = floor(Random() * ((max-min)+1));
	number += min;
	if(number > max){
		number = max;
	}
	return number;

}




void randomizeSupply(struct gameState *g){
	int i;
	for(i=0; i < MAX_SUPPLY; i++){
		g->supplyCount[i] = getRandomNumber(0, 40);
	}
}

void randomizeHands(struct gameState *g){
	int players = g->numPlayers;
	int i, j;
	for(i=0; i < players; i++){
		g->handCount[i] = getRandomNumber(0, MAX_HAND);
		for(j = 0; j < g->handCount[i]; j++){
			g->hand[i][j] = getRandomNumber(0, MAX_SUPPLY);
			if(g->hand[i][j] == MAX_SUPPLY) g->hand[i][j] = 26;
		}

	}
}

void randomizeDecks(struct gameState *g){
	int players = g->numPlayers;
	int i, j;
	for(i=0; i < players; i++){
		g->deckCount[i] = getRandomNumber(0, MAX_DECK);
		for(j = 0; j < g->deckCount[i]; j++){
			g->deck[i][j] = getRandomNumber(0, MAX_SUPPLY);
			if(g->deck[i][j] == MAX_SUPPLY) g->deck[i][j] = 26;
		}

	}
	
}

void randomizeDiscard(struct gameState *g){
	int players = g->numPlayers;
	int i, j;
	for(i=0; i < players; i++){
		g->discardCount[i] = getRandomNumber(0, MAX_DECK);
		for(j = 0; j < g->discardCount[i]; j++){
			g->discard[i][j] = getRandomNumber(0, MAX_SUPPLY);
			if(g->discard[i][j] == MAX_SUPPLY) g->discard[i][j] = 26;
		}

	}
	
}
void randomizeTurn(struct gameState *g){
	g->whoseTurn = getRandomNumber(0, g->numPlayers);
}

void randomizePlayed(struct gameState *g){
	int played = getRandomNumber(0, 10);
	int i;
	for(i=0; i<played; i++){
		g->playedCards[i] = getRandomNumber(0, MAX_SUPPLY);
	}
	g->playedCardCount = played;
}
//randomizeTurn() should be called before calling this


int makeRandomGameState(struct gameState *g){
//	printf("Randomizing Game\n");
	g->numPlayers = getRandomNumber(2, 5);
	if(g->numPlayers == 5) g->numPlayers = 4;
	randomizeSupply(g);
	randomizeDecks(g);
	randomizeDiscard(g);
	randomizeHands(g);
	randomizeTurn(g);
	randomizePlayed(g);
	g->coins = getRandomNumber(0, 20);
	g->numActions = getRandomNumber(0, 5);
	g->numBuys = getRandomNumber(1, 5);
//	printf("Game Randomized\n");
	return 0;
}
int *countAllCards(struct gameState *g, int which){
	assert(which > 0 && which < 6);
	int whoseTurn = g->whoseTurn;
	int deckCount = g->deckCount[whoseTurn];
	int discardCount = g->discardCount[whoseTurn];
	int handCount = g->handCount[whoseTurn];
	int playedCardCount = g->playedCardCount;
	int* allCards = malloc(sizeof(int) * MAX_SUPPLY);

	int i, j;
	for(i=0; i<MAX_SUPPLY; i++){
		allCards[i] = 0;  //just to be sure
	}
	for (i=0; i<MAX_SUPPLY; i++){
		if(which == 1 || which == 5){
			for(j=0; j < handCount; j++){
				if (g->hand[whoseTurn][j] == i){
					allCards[i]++;
				}
			}
		}
		if(which == 2 || which == 5){
			for(j=0; j < deckCount; j++){
				if (g->deck[whoseTurn][j] == i){
					allCards[i]++;
				}
			}
		}
		if(which == 3 || which == 5){
			for(j = 0; j< discardCount; j++){
				if(g->discard[whoseTurn][j] == i){
					allCards[i]++;
				}
			}
		}
		if(which == 4 || which == 5){
			for(j = 0; j < playedCardCount; j++){
				if(g->discard[whoseTurn][j] == i){
					allCards[i]++;
				}
			}
		}
	}
	return allCards;
}

//returns an array of totals of all cards in deck, discard and hand
//from a specific player
int *countAllOtherCards(struct gameState *g, int which, int player){
	assert(which > 0 && which < 5);
	assert(player >=0 && player < 5);
	int deckCount = g->deckCount[player];
	int discardCount = g->discardCount[player];
	int handCount = g->handCount[player];

	int* allCards = malloc(sizeof(int) * MAX_SUPPLY);

	int i, j;
	for(i=0; i<MAX_SUPPLY; i++){
		allCards[i] = 0;  //just to be sure
	}
	for (i=0; i<MAX_SUPPLY; i++){
		if(which == 1 || which == 4){
			for(j=0; j < handCount; j++){
				if (g->hand[player][j] == i){
					allCards[i]++;
				}
			}
		}
		if(which == 2 || which == 4){
			for(j=0; j < deckCount; j++){
				if (g->deck[player][j] == i){
					allCards[i]++;
				}
			}
		}
		if(which == 3 || which == 4){
			for(j = 0; j< discardCount; j++){
				if(g->discard[player][j] == i){
					allCards[i]++;
				}
			}
		}
		
	}
	return allCards;
}

int compareSet(int *set1, int *set2){
	int i;
	int pass = 1;
	for(i=0; i<MAX_SUPPLY; i++){
		if(set1[i] != set2[i]){
			printf("card count for %d: set1: %d set2: %d\n", i, set1[i],set2[i]);
			//return 0;
			pass = 0;
		}
	}
	if (pass == 1)
		return 1;
	else
		return 0;
}

int **getOtherCardCounts(struct gameState *g, int which){
	//int i;
	int **playerSet = malloc(sizeof(int*) * 4);
	playerSet[0] = countAllOtherCards(g, which, 0);
	playerSet[1] = countAllOtherCards(g, which, 1);
	playerSet[2] = countAllOtherCards(g, which, 2);
	playerSet[3] = countAllOtherCards(g, which, 3);	
	//int **playerSet = {p0, p1, p2, p3};
	return playerSet;
}

void printSet(int *set){
	int i;
	for(i=0; i < MAX_SUPPLY; i++){
		printf("card: %d amt: %d\n", i, set[i]);
	}
}

int CouncilCardsInPlayed(struct gameState *g){
	int i;
	int count = 0;
	for(i=0; i < g->playedCardCount; i++){
		if(g->playedCards[i] == council_room){
			count ++;
		}
	}
	return count;
}


int testCouncilRoom(struct gameState *g, int *testFailure){
	int i;
	int actionsBefore = g->numActions;
	int buysBefore = g->numBuys;

	//For the other players
	int handsBefore[g->numPlayers];
	int decksBefore[g->numPlayers];
	int discardsBefore[g->numPlayers];
	for(i=0; i < g->numPlayers; i++){
		handsBefore[i] = g->handCount[i];
		decksBefore[i] = g->deckCount[i];
		discardsBefore[i] = g->discardCount[i];
	}


	//make sure there is at least one card in the hand and card being played is in it.
	int handCount = g->handCount[g->whoseTurn];
	if(handCount <= 0){
		handCount = 1;
		g->handCount[g->whoseTurn] = handCount;
	}
	int handPos = getRandomNumber(0, (handCount - 1));
	g->hand[g->whoseTurn][handPos] = council_room;

	int choice1 = getRandomNumber(0, 5);
	int choice2 = getRandomNumber(0, 5);
	int choice3 = getRandomNumber(0, 5);
	
	int playedBefore = CouncilCardsInPlayed(g);
	int cardsInHandBefore = g->handCount[g->whoseTurn];
	

	struct gameState cpy;
	struct gameState *copy = &cpy;

	int *allCardsBefore = countAllCards(g, 5);
	int **otherCardsBefore = getOtherCardCounts(g, 4);
	int **otherHandsBefore = getOtherCardCounts(g, 1);

	memcpy(copy, g, sizeof(struct gameState));

	int b = 0;
	int *bonus = &b;
	int result = cardEffect(council_room, choice1, choice2, choice3, g, handPos, bonus);
	if(result == -1){
		if(memcmp(copy, g, sizeof(struct gameState)) != 0){
			printf("Gamestate changed on negative result\n");
			testFailure[0]++;
		}
		return -1;
	}

	int *allCardsAfter = countAllCards(g, 5);
	//Test for post-requirement 1
	printf("CURRENT PLAYER SET\n");
	if(!compareSet(allCardsBefore, allCardsAfter)){
		testFailure[1]++;
	//	printf("Total Card count changed\n");
		//printf("Before:\n");
		//printSet(allCardsBefore);
		//printf("After:\n");
		//printSet(allCardsAfter);
	}
	printf("OTHER PLAYER SETS\n");
	int **otherCardsAfter = getOtherCardCounts(g, 4);
	int **otherHandsAfter = getOtherCardCounts(g, 1);
	for(i=0; i<g->numPlayers; i++){
		if(i != g->whoseTurn){
			if(!compareSet(otherCardsBefore[i], otherCardsAfter[i])){
				testFailure[2]++;
			}
		}
	}
	int playedAfter = CouncilCardsInPlayed(g);
	if(playedAfter != (playedBefore +1)){
		printf("Test 3: Council cards in cardsPlayed Before: %d After: %d\n", playedBefore, playedAfter);
		testFailure[3]++;
	}
	int cardsInHandAfter = g->handCount[g->whoseTurn];
	if(cardsInHandAfter != (cardsInHandBefore + 3)){
		if((g->deckCount[g->whoseTurn] + g->discardCount[g->whoseTurn]) != 0){
			printf("Test 4: Current Player's cards in hand Before: %d After %d\n", cardsInHandBefore, cardsInHandAfter);
			testFailure[4]++;

		}
	}
	int actionsAfter = g->numActions;
	if(actionsBefore != actionsAfter){
		printf("Test 5: Actions Before: %d After: %d\n", actionsBefore, actionsAfter);
		testFailure[5]++;
	}
	int buysAfter = g->numBuys;
	if(buysAfter != (buysBefore +1)){
		printf("Test 6: numBuys Before: %d After: %d", buysBefore, buysAfter);
		testFailure[6]++;
	}

	for(i=0; i < g->numPlayers; i++){
		if(i != g->whoseTurn){
			if((handsBefore[i] +1)!= g->handCount[i]){
				printf("Test 7: \nPlayer %d cards in hand Before: %d After: %d\n", i, handsBefore[i], g->handCount[i]);
				printf("Player %d cards in deck Before: %d After: %d\n", i, decksBefore[i], g->deckCount[i]);
				printf("Player %d cards in discard Before: %d After: %d\n", i, discardsBefore[i], g->discardCount[i]);
			//	printf("\nHand before\n");
			//	printSet(otherHandsBefore[i]);
			//	printf("\nHand After\n");
			//	printSet(otherHandsAfter[i]);
				testFailure[7]++;
			//	return 0;
			}
			if(decksBefore[i] > 0){
				if((decksBefore[i] - 1)!= g->deckCount[i]){
					printf("Player %d cards in deck Before: %d After: %d\n", i, decksBefore[i], g->deckCount[i]);
					printf("Player %d cards in hand Before: %d After: %d\n", i, handsBefore[i], g->handCount[i]);
					printf("Player %d cards in discard Before: %d After: %d\n", i, discardsBefore[i], g->discardCount[i]);
					testFailure[7]++;
			//		return 0;
				}
				if(discardsBefore[i] != g->discardCount[i]){
					printf("Player %d cards in discard Before: %d After: %d\n", i, discardsBefore[i], g->discardCount[i]);
					printf("Player %d cards in hand Before: %d After: %d\n", i, handsBefore[i], g->handCount[i]);
					printf("Player %d cards in deck Before: %d After: %d\n", i, decksBefore[i], g->deckCount[i]);
					testFailure[7]++;
			//		return 0;

				}
			}
			else{
				if(discardsBefore[i] > 0){
					if(discardsBefore[i] -1 != g->deckCount[i]){
						printf("player %d Discard Before: %d After: %d, Deck Before: %d After: %d\n", i, discardsBefore[i], g->discardCount[i], decksBefore[i], g->deckCount[i]);
						testFailure[8]++;
					}
				}
			}
		}
	}
	return 1;
}

int main(int argc, char** argv){
	int strm = 1, seed = 1, min = 2, max = 50;
	if(argc > 1){
		strm = atoi(argv[1]);
	}
	if(argc > 2){
		seed = atoi(argv[2]);
	}
	if(argc > 3){
		min = atoi(argv[3]);
	}
	if(argc > 4){
		max = atoi(argv[4]);
	}
	prepRandom(strm, seed);
	int tests = 9;
	int testFailure[tests];
	int i;
	for(i=0; i < tests; i++){
		testFailure[i] = 0;
	}
	struct gameState *g = newGame();
	printf("Testing Council Room.  It adds 4 cards to the hand, adds one buy, and adds one card to each other player's hand\n");
	int testRuns = 100;
	printf("Running %d runs with random gamestates\n", testRuns);
	for(i=0; i < testRuns; i++){
	
		makeRandomGameState(g);
		if(!testCouncilRoom(g, testFailure)){
			printf("current player = %d", g->whoseTurn);

			break;

		}
	}
	for(i=0; i < tests; i++){
		printf("Test %d failed %d times\n", i, testFailure[i]);
	}
	printf("Test Complete\n");

	return 0;
}
